home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS05.ADF / IFF / readpict.c < prev    next >
C/C++ Source or Header  |  1986-04-20  |  11KB  |  291 lines

  1.  
  2. /** ReadPict.c **************************************************************
  3.  *
  4.  * Read an ILBM raster image file.                     23-Jan-86.
  5.  *
  6.  * By Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  7.  * This software is in the public domain.
  8.  *
  9.  * USE THIS AS AN EXAMPLE PROGRAM FOR AN IFF READER.
  10.  *
  11.  * The IFF reader portion is essentially a recursive-descent parser.
  12.  ****************************************************************************/
  13.  
  14. #define LOCAL  static
  15.  
  16. #include "iff/intuall.h"
  17. #include "libraries/dos.h"
  18. #include "libraries/dosextens.h"
  19. #include "iff/ilbm.h"
  20. #include "iff/readpict.h"
  21.  
  22. /* This example's max number of planes in a bitmap. Could use MaxAmDepth. */
  23. #define EXDepth 5
  24. #define maxColorReg (1<<EXDepth)
  25. #define MIN(a,b) ((a)<(b)?(a):(b))
  26.  
  27. #define SafeFreeMem(p,q) {if(p)FreeMem(p,q);}
  28.  
  29. /* Define the size of a temporary buffer used in unscrambling the ILBM rows.*/
  30. #define bufSz 512
  31.  
  32. /*------------ ILBM reader -----------------------------------------------*/
  33. /* ILBMFrame is our "client frame" for reading FORMs ILBM in an IFF file.
  34.  * We allocate one of these on the stack for every LIST or FORM encountered
  35.  * in the file and use it to hold BMHD & CMAP properties. We also allocate
  36.  * an initial one for the whole file.
  37.  * We allocate a new GroupContext (and initialize it by OpenRIFF or
  38.  * OpenRGroup) for every group (FORM, CAT, LIST, or PROP) encountered. It's
  39.  * just a context for reading (nested) chunks.
  40.  *
  41.  * If we were to scan the entire example file outlined below:
  42.  *    reading          proc(s)                new               new
  43.  *
  44.  * --whole file--   ReadPicture+ReadIFF   GroupContext        ILBMFrame
  45.  * CAT              ReadICat                GroupContext
  46.  *   LIST           GetLiILBM+ReadIList       GroupContext        ILBMFrame
  47.  *     PROP ILBM    GetPrILBM                   GroupContext
  48.  *       CMAP       GetCMAP
  49.  *       BMHD       GetBMHD
  50.  *     FORM ILBM    GetFoILBM                   GroupContext        ILBMFrame
  51.  *       BODY       GetBODY
  52.  *     FORM ILBM    GetFoILBM                   GroupContext        ILBMFrame
  53.  *       BODY       GetBODY
  54.  *   FORM ILBM      GetFoILBM                 GroupContext        ILBMFrame
  55.  */
  56.  
  57. /* NOTE: For a small version of this program, set Fancy to 0.
  58.  * That'll compile a program that reads a single FORM ILBM in a file, which
  59.  * is what DeluxePaint produces. It'll skip all LISTs and PROPs in the input
  60.  * file. It will, however, look inside a CAT for a FORM ILBM.
  61.  * That's suitable for 90% of the uses.
  62.  *
  63.  * For a fancier version that handles LISTs and PROPs, set Fancy to 1.
  64.  * That'll compile a program that dives into a LIST, if present, to read
  65.  * the first FORM ILBM. E.g. a DeluxePrint library of images is a LIST of
  66.  * FORMs ILBM.
  67.  *
  68.  * For an even fancier version, set Fancy to 2. That'll compile a program
  69.  * that dives into non-ILBM FORMs, if present, looking for a nested FORM ILBM.
  70.  * E.g. a DeluxeVideo C.S. animated object file is a FORM ANBM containing a
  71.  * FORM ILBM for each image frame. */
  72. #define Fancy  0
  73.  
  74. /* Global access to client-provided pointers.*/
  75. LOCAL Allocator *gAllocator = NULL;
  76. LOCAL struct BitMap *gBM = NULL;   /* client's bitmap.*/
  77. LOCAL ILBMFrame *giFrame = NULL;   /* "client frame".*/
  78.  
  79. /** GetFoILBM() *************************************************************
  80.  *
  81.  * Called via ReadPicture to han`0Ele every FORM encountered in an IFF file.
  82.  * Reads FORMs ILBM and skips all others.
  83.  * Inside a FORM ILBM, it stops once it reads a BODY. It complains if it
  84.  * finds no BODY or if it has no BMHD to decode the BODY.
  85.  *
  86.  * Once we find a BODY chunk, we'll allocate the BitMap and read the image.
  87.  *
  88.  ****************************************************************************/
  89. LOCAL BYTE bodyBuffer[bufSz];
  90. IFFP GetFoILBM(parent)  GroupContext *parent;  {
  91.    /*compilerBug register*/ IFFP iffp;
  92.    GroupContext formContext;
  93.    ILBMFrame ilbmFrame;       /* only used for non-clientFrame fields.*/
  94.    register int i;
  95.    LONG plsize;     /* Plane size in bytes. */
  96.    int nPlanes; /* number of planes in our display image */
  97.  
  98.     /* Handle a non-ILBM FORM. */
  99.     if (parent->subtype != ID_ILBM) {
  100. #if Fancy >= 2
  101.      /* Open a non-ILBM FORM and recursively scan it for ILBMs.*/
  102.      iffp = OpenRGroup(parent, &formContext);
  103.      CheckIFFP();
  104.      do {
  105.          iffp = GetF1ChunkHdr(&formContext);
  106.          } while (iffp >= IFF_OKAY);
  107.      if (iffp == END_MARK)
  108.          iffp = IFF_OKAY;     /* then continue scanning the file */
  109.      CloseRGroup(&formContext);
  110.      return(iffp);
  111. #else
  112.         return(IFF_OKAY); /* Just skip this FORM and keep scanning the file.*/
  113. #endif
  114.      }
  115.  
  116.    ilbmFrame = *(ILBMFrame *)parent->clientFrame;
  117.    iffp = OpenRGroup(parent, &formContext);
  118.    CheckIFFP();
  119.  
  120.    do switch (iffp = GetFChunkHdr(&formContext)) {
  121.       case ID_BMHD: {
  122.      ilbmFrame.foundBMHD = TRUE;
  123.      iffp = GetBMHD(&formContext, &ilbmFrame.bmHdr);
  124.      break; }
  125.       case ID_CMAP: {
  126.      ilbmFrame.nColorRegs = maxColorReg;  /* we have room for this many */
  127.      iffp = GetCMAP(
  128.         &formContext, (WORD *)&ilbmFrame.colorMap[0], &ilbmFrame.nColorRegs);
  129.                /* was &ilbmFrame.colorMap, (fixed) robp. */
  130.      break; }
  131.       case ID_BODY: {
  132.          if (!ilbmFrame.foundBMHD)  return(BAD_FORM);  /* No BMHD chunk! */
  133.  
  134.       nPlanes = MIN(ilbmFrame.bmHdr.nPlanes, EXDepth);
  135.       InitBitMap(
  136.          gBM,
  137.          nPlanes,
  138.          ilbmFrame.bmHdr.w,
  139.          ilbmFrame.bmHdr.h);
  140.       plsize = RowBytes(ilbmFrame.bmHdr.w) * ilbmFrame.bmHdr.h;
  141.       /* Allocate all planes contiguously.  Not really necessary,
  142.        * but it avoids writing code to back-out if only enough memory
  143.        * for some of the planes.
  144.        * WARNING: Don't change this without changing the code that
  145.        * Frees these planes.
  146.        */
  147.       if (gBM->Planes[0] =
  148.           (PLANEPTR)(*gAllocator)(nPlanes * plsize))
  149.          {
  150.          for (i = 1; i < nPlanes; i++)
  151.           gBM->Planes[i] = (PLANEPTR) gBM->Planes[0] + plsize*i;
  152.          iffp = GetBODY(
  153.           &formContext,
  154.           gBM,
  155.           NULL,
  156.           &ilbmFrame.bmHdr,
  157.           bodyBuffer,
  158.           bufSz);
  159.          if (iffp == IFF_OKAY) iffp = IFF_DONE;   /* Eureka */
  160.          *giFrame = ilbmFrame;  /* Copy fields to client's frame.*/
  161.          }
  162.       else 
  163.          iffp = CLIENT_ERROR; /* not enough RAM for the bitmap */
  164.          break; }
  165.       case END_MARK: { iffp = BAD_FORM; break; } /* No BODY chunk! */
  166.       } while (iffp >= IFF_OKAY);  /* loop if valid ID of ignored chunk or a
  167.                  * subroutine returned IFF_OKAY (no errors).*/
  168.  
  169.    if (iffp != IFF_DONE)  return(iffp);
  170.  
  171.    /* If we get this far, there were no errors. */
  172.    CloseRGroup(&formContext);
  173.    return(iffp);
  174.    }
  175.  
  176. /** Notes on extending GetFoILBM ********************************************
  177.  *
  178.  * To read more kinds of chunks, just add clauses to the switch statement.
  179.  * To read more kinds of property chunks (GRAB, CAMG, etc.) add clauses to
  180.  * the switch statement in GetPrILBM, too.
  181.  *
  182.  * To read a FORM type that contains a variable number of data chunks--e.g.
  183.  * a FORM FTXT with any number of CHRS chunks--replace the ID_BODY case with
  184.  * an ID_CHRS case that doesn't set iffp = IFF_DONE, and make the END_MARK
  185.  * case do whatever cleanup you need.
  186.  *
  187.  ****************************************************************************/
  188.  
  189. /** GetPrILBM() *************************************************************
  190.  *
  191.  * Called via ReadPicture to handle every PROP encountered in an IFF file.
  192.  * Reads PROPs ILBM and skips all others.
  193.  *
  194.  ****************************************************************************/
  195. #if Fancy
  196. IFFP GetPrILBM(parent)  GroupContext *parent;  {
  197.    /*compilerBug register*/ IFFP iffp;
  198.    GroupContext propContext;
  199.    ILBMFrame *ilbmFrame = (ILBMFrame *)parent->clientFrame;
  200.  
  201.    if (parent->subtype != ID_ILBM)
  202.       return(IFF_OKAY);  /* just continue scaning the file */
  203.  
  204.    iffp = OpenRGroup(parent, &propContext);
  205.    CheckIFFP();
  206.  
  207.    do switch (iffp = GetPChunkHdr(&propContext)) {
  208.       case ID_BMHD: {
  209.      ilbmFrame->foundBMHD = TRUE;
  210.      iffp = GetBMHD(&propContext, &ilbmFrame->bmHdr);
  211.      break; }
  212.       case ID_CMAP: {
  213.      ilbmFrame->nColorRegs = maxColorReg; /* we have room for this many */
  214.      iffp = GetCMAP(
  215.        &propContext, (WORD *)&ilbmFrame->colorMap, &ilbmFrame->nColorRegs);
  216.      break; }
  217.       } while (iffp >= IFF_OKAY);  /* loop if valid ID of ignored chunk or a
  218.                  * subroutine returned IFF_OKAY (no errors).*/
  219.  
  220.    CloseRGroup(&propContext);
  221.    return(iffp == END_MARK ? IFF_OKAY : iffp);
  222.    }
  223. #endif
  224.  
  225. /** GetLiILBM() *************************************************************
  226.  *
  227.  * Called via ReadPicture to handle every LIST encountered in an IFF file.
  228.  *
  229.  ****************************************************************************/
  230. #if Fancy
  231. IFFP GetLiILBM(parent)  GroupContext *parent;  {
  232.     ILBMFrame newFrame;  /* allocate a new Frame */
  233.  
  234.     newFrame = *(ILBMFrame *)parent->clientFrame;  /* copy parent frame */
  235.  
  236.     return( ReadIList(parent, (ClientFrame *)&newFrame) );
  237.     }
  238. #endif
  239.  
  240. /** ReadPicture() **********************************************************/
  241. IFFP ReadPicture(file, bm, iFrame, allocator)
  242.    LONG file;
  243.    struct BitMap *bm;
  244.    ILBMFrame *iFrame;    /* Top level "client frame".*/
  245.  
  246.      /* **** ERROR IN SOURCE CODE, WAS jFrame, now iFrame */
  247.      /* fixed */
  248.  
  249.    Allocator *allocator;
  250.    {
  251.    IFFP iffp = IFF_OKAY;
  252.  
  253. #if Fancy
  254.    iFrame->clientFrame.getList = GetLiILBM;
  255.    iFrame->clientFrame.getProp = GetPrILBM;
  256. #else
  257.    iFrame->clientFrame.getList = SkipGroup;
  258.    iFrame->clientFrame.getProp = SkipGroup;
  259. #endif
  260.    iFrame->clientFrame.getForm = GetFoILBM;
  261.    iFrame->clientFrame.getCat  = ReadICat ;
  262.  
  263.    /* Initialize the top-level client frame's property settings to the
  264.     * program-wide defaults. This example just records that we haven't read
  265.     * any BMHD property or CMAP color registers yet. For the color map, that
  266.     * means the default is to leave the machine's color registers alone.
  267.     * If you want to read a property like GRAB, init it here to (0, 0). */
  268.    iFrame->foundBMHD  = FALSE;
  269.    iFrame->nColorRegs = 0;
  270.  
  271.    gAllocator = allocator;
  272.    gBM = bm;
  273.    giFrame = iFrame;
  274.   /* Store a pointer to the client's frame in a global variable so that
  275.    * GetFoILBM can update client's frame when done.  Why do we have so
  276.    * many frames & frame pointers floating around causing confusion?
  277.    * Because IFF supports PROPs which apply to all FORMs in a LIST,
  278.    * unless a given FORM overrides some property.  
  279.    * When you write code to read several FORMs,
  280.    * it is ssential to maintain a frame at each level of the syntax
  281.    * so that the properties for the LIST don't get overwritten by any
  282.    * properties specified by individual FORMs.
  283.    * We decided it was best to put that complexity into this one-FORM example,
  284.    * so that those who need it later will have a useful starting place.
  285.    */
  286.  
  287.    iffp = ReadIFF(file, (ClientFrame *)iFrame);
  288.    return(iffp);
  289.    }
  290.  
  291.